//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================
#include <ddk.h>
#include "CSocketService.h"
#define IncrementDllLockCount() do {} while(0)
#define DecrementDllLockCount() do {} while(0)
#include "_CSocketService.cpp"

//#include <sys/netmisc.h>
#include "CSocket.h"
#include <eltypes.h>
#include <stdio.h>
#include "lwip/netif.h"
#include "lwip/sockets.h"

#define    ENDPOINT_READABLE    ( 0x1 )
#define    ENDPOINT_WRITEABLE   ( 0x2 )
#define    ENDPOINT_EXCEPTIONAL ( 0x4 )

#ifdef _win32
Boolean g_HaveCommModule = FALSE;
#include "CWin32SocketService.h"
#endif

extern "C" int g_errno;
UInt32 CSocketService::AddRef()
{
//    Int32 nRef = 1;
//    return (UInt32)nRef;
    UInt32 Refs=InterlockedIncrement(&m_cRef);
//    printf("add=%d\n", Refs);
    return (UInt32)Refs;
}

UInt32 CSocketService::Release()
{
//    Int32 nRef = 1;
    UInt32 Refs;
    Refs = (UInt32)InterlockedDecrement(&m_cRef);
    if (Refs == 0) {
        delete this;
    }
//    printf("rel=%d\n", Refs);
    return Refs;
}

ECode CSocketService::constructor()
{
    return NOERROR;
}

ECode CSocketService::CreateSocket(
        /* [in] */ Int32 family,
        /* [in] */ Int32 type,
        /* [in] */ Int32 protocol,
        /* [out] */ ISocket ** ppNewSocket,
        /* [out] */ Int32 *pError)
{
    // TODO: Add your code here
    int so;
    CSocket *pSocket;

    so = lwip_socket(family, type, protocol);
    if (so == -1) {
        *pError = g_errno;
        return S_FALSE;
    }
    pSocket = new CSocket(so);
    pSocket->AddRef();
    if (pSocket == NULL) {
        pSocket->Release();
        return E_NOT_ENOUGH_MEMORY;
    }
//    kprintf("Createso=%d\n", so);
    *ppNewSocket = pSocket;
    return NOERROR;
}

ECode CSocketService::WaitForSockets(
    /* [in] */ const SocketTime & timeout,
    /* [in] */ const BufferOf<IInterface*> & sockets,
    /* [in] */ const MemoryBuf & eventRequests,
    /* [out] */ MemoryBuf * pEventResponses,
    /* [out] */ Int32 *pError)
{
    int Loop;

    int max_fd = 0;
    int n;
    struct timeval time;
    fd_set input;
    fd_set output;
    fd_set except;
    FD_ZERO(&input);
    FD_ZERO(&output);
    FD_ZERO(&except);
    int NumezSockets = sockets.GetUsed();
//    max_fd = NumezSockets;
    for (Loop = 0; Loop < NumezSockets; Loop++) {
        if (((CSocket*)sockets[Loop])->m_socket >  max_fd) {
            max_fd = ((CSocket*)sockets[Loop])->m_socket;
        }
//        ezEventRequests[Loop] &= 0x07;
        if (eventRequests[Loop] & ENDPOINT_READABLE) {
            FD_SET(((CSocket*)sockets[Loop])->m_socket, &input);
        }
        if (eventRequests[Loop] & ENDPOINT_WRITEABLE) {
            FD_SET(((CSocket*)sockets[Loop])->m_socket, &output);
        }
        if (eventRequests[Loop] & ENDPOINT_EXCEPTIONAL) {
            FD_SET(((CSocket*)sockets[Loop])->m_socket, &except);
        }
    }
//    time.tv_sec = timeout/(1000*1000*10);
//    time.tv_usec = (timeout - time.tv_sec)/10;
    time.tv_sec = timeout.time_sec;
    time.tv_usec = timeout.time_usec;
    n = lwip_select(max_fd+1, &input, &output, &except, &time);

    if (pEventResponses == NULL) {
        return E_INVALID_ARGUMENT;
    }
    pEventResponses->Copy(eventRequests.GetPayload(), NumezSockets);
    if (n > 0) {
        for (Loop = 0;Loop < NumezSockets;Loop++) {
            if (FD_ISSET(((CSocket*)sockets[Loop])->m_socket, &input)) {
                (*pEventResponses)[Loop] |= ENDPOINT_READABLE << 3;
            }
            if (FD_ISSET(((CSocket*)sockets[Loop])->m_socket, &output)) {
                (*pEventResponses)[Loop] |= ENDPOINT_WRITEABLE << 3;
            }
            if (FD_ISSET(((CSocket*)sockets[Loop])->m_socket, &except)) {
                (*pEventResponses)[Loop] |= ENDPOINT_EXCEPTIONAL << 3;
            }
        }
    }
    if (n < 0) {
        kprintf("select failed.\n");
        return NOERROR;
    }
    if (n == 0) {
//        kprintf("select timeout.\n");
        return S_TIMED_OUT;
    }
    *pError = n;
    /*
     * Values returned in ezEventRequests array are
     * only those conditions which were satisfied.
     */
    for (Loop = 0; Loop < NumezSockets; Loop++) {
        (*pEventResponses)[Loop] >>= 3;
    }

    return NOERROR;
}

ECode CSocketService::GetIPAddresses(
    /* [out] */ IPv4Address *ipAddr,
    /* [out] */ IPv4Address *netmask,
    /* [out] */ IPv4Address *gateway,
    /* [out] */ IPv4Address *dns1,
    /* [out] */ IPv4Address *dns2)
{
    struct netif *netif = netif_get_default();
    if (!netif) { return E_DOES_NOT_EXIST; }
    memcpy(ipAddr, &netif->ip_addr, sizeof(IPv4Address));
    memcpy(netmask, &netif->netmask, sizeof(IPv4Address));
    memcpy(gateway, &netif->gw, sizeof(IPv4Address));
    memcpy(dns1, &netif->dns1, sizeof(IPv4Address));
    memcpy(dns2, &netif->dns2, sizeof(IPv4Address));

    return NOERROR;
}

extern int RegisterInterface()
{
    ECode ec = NOERROR;
    ISocketService *iSockservice = NULL;

#ifdef _win32
    if (g_HaveCommModule) {
        iSockservice = new CSocketService;
    }
    else {
        iSockservice = new CWin32SocketService;
        if (NULL != iSockservice) {
            ec = ((CWin32SocketService *)iSockservice)->constructor();
            if (FAILED(ec)) {
                assert(0 && "Initialize Win32Socket Failed!!\n");
            }
        }
    }
#else
    iSockservice = new CSocketService;
#endif

    assert(NULL != iSockservice);

    iSockservice->AddRef();
  //  iSockservice->constructor();
    kprintf("in net service\n");

    ec = DzRegisterService(L"tcpip",
                          (IInterface *)(ISocketService*)iSockservice,
                          NULL);
    if (FAILED(ec)) {
        kprintf("*WARNING* Failed to register: CSocketFactoryRef.\n");
        return 0;
    }
    return 0;
}

